# -*- coding: UTF-8 -*-
import json
import logging
import os
import random
import socket
import struct
import thread
import threading
import time
from multiprocessing import Pool

import IPy
import yaml

import conf
import http_responser
import log_helper
from utils import ip_utils
from utils import utils

logger = logging.getLogger('tcp')

port1 = 1600
port2 = 600

file_lock = threading.Lock()


def to_bcd(int_value):
    hex_value = hex(int_value)
    bcd = str(hex_value).replace('0x', '')
    if len(bcd) == 1:
        bcd = '0' + bcd
    dest = bcd[::-1]
    # if dest[1] == 'f':
    #     dest = dest[0]
    return dest


def str_to_bcd(str_val):
    ret_arr = []
    for i in range(0, len(str_val), 2):
        if i == (len(str_val) - 1):
            ret_arr.append('f' + str_val[i])
        else:
            ret_arr.append(str_val[i + 1] + str_val[i])
    if len(str_val) % 2 == 0:
        ret_arr.append('0f')
    return ''.join(ret_arr)


def fix_arr_length(orgin_data, data_length):
    result_arr = [0] * data_length
    for idx, data in enumerate(orgin_data):
        result_arr[idx] = data
    return result_arr


def to_normal(int_value):
    dest = hex(int_value)
    dest = str(dest).replace('0x', '')
    if len(dest) == 1:
        dest = '0' + dest
    if dest[1] == 'f':
        dest = dest[0]
    if dest == 'ff':
        dest = ''
    return dest


def to_normal2(int_value):
    dest = hex(int_value)
    dest = str(dest).replace('0x', '')
    if len(dest) == 1:
        dest = '0' + dest
    # if dest[1] == 'f':
    #     dest = dest[0]
    # if dest == 'ff':
    #     dest = ''
    return dest


def int_to_hex_str(int_value):
    dest = hex(int_value)
    dest = str(dest).replace('0x', '')
    if len(dest) == 1:
        return "0" + dest
    return dest


def hex_to_int(hex_val):
    ret_arr = []
    for i in range(0, len(hex_val), 2):
        if i != (len(hex_val) - 1):
            ret_arr.append(int(hex_val[i] + hex_val[i + 1], 16))
        else:
            ret_arr.append(int(hex_val[i], 16))
    return ret_arr


def little2big_endian(hex_string):
    big_endian_str = ''
    for i in range(len(hex_string) / 4):
        little_endian_char = hex_string[i * 4: i * 4 + 4]
        big_endian_char = little_endian_char[2:4] + little_endian_char[0:2]  # 逆字节序
        big_endian_str = big_endian_str + big_endian_char
    return big_endian_str


def hex2hanzi(hex_string):
    unicode_hanzi = ''  # 定义一个空字符串
    for i in range(len(hex_string) / 4):  # 因为Unicode是4个字符表示一个汉字，每四个一组
        hex_char = hex_string[i * 4: i * 4 + 4]
        if hex_char == '0000':
            hex_char = '0040'
        if hex_char == '007f':
            hex_char = '00e0'
        unicode_char = "\\u" + hex_char
        unicode_hanzi = unicode_hanzi + unicode_char
    return unicode_hanzi.decode('unicode_escape')


def str2hex(s):
    if not isinstance(s, unicode):
        s = s.decode('utf-8')
    result = ''
    for c in s:
        print c, ord(c)
        tmp = repr(c).replace("u'", "", 1).replace("\\u", '').replace("\\x", '00').replace("'", '')
        if len(tmp) == 1:
            result += '00' + hex(ord(c)).replace('0x', '')
        elif ord(c) < 16:
            result += '000' + hex(ord(c)).replace('0x', '')
        else:
            result += tmp
    return result


def str2hex2(s):
    if not isinstance(s, unicode):
        s = s.decode('utf-8')
    result = ''
    for c in s:
        if c != 'u':
            tmp = repr(c).replace("u'", "", 1).replace("\\u", '').replace("\\x", '00').replace("'", '')
        else:
            tmp = 'u'
        print c, tmp
        if len(tmp) == 1:
            result += hex(ord(c)).replace('0x', '')
        elif ord(c) < 16:
            result += '0' + hex(ord(c)).replace('0x', '')
        else:
            result += tmp
    return result


def ascii2hanzi(hex_string):
    unicode_hanzi = ''
    for i in range(0, len(hex_string), 2):
        if i != len(hex_string):
            tmp_int = int(hex_string[i] + hex_string[i + 1], 16)
            unicode_hanzi += chr(tmp_int)
    return unicode_hanzi


def query_conf(conf_file=None):
    if conf_file is None:
        conf_file = conf.conf_yaml_path
    with open(conf_file, 'r') as f:
        yaml_data_str = f.read()
    all_data = yaml.load(yaml_data_str, Loader=yaml.FullLoader)
    return all_data


def post_local_data(data, port=None):
    if port is None:
        port = conf.sending_udp_port
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.settimeout(20)
    s.sendto(data, (conf.sending_udp_host, port))
    s.close()


def post_sms_data(data, port=None):
    if port is None:
        port = conf.sms_sending_port
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.settimeout(20)
    s.sendto(data, (conf.sms_sending_host, port))
    s.close()


def get_content_length(sms_content):
    sms_hex_len = hex(len(sms_content)).replace('0x', '')
    if len(sms_hex_len) > 2:
        return [int(sms_hex_len[-2:], 16), int(sms_hex_len[0:-2], 16)]
    else:
        return [len(sms_content), 0]


def recive_sms_info(request_json):
    """
    下发短信
    :param request_json:
    :return:
    """
    send_phone = str(request_json['params']['sendPhone'])
    calling_msisdn = hex_to_int(str_to_bcd(send_phone))
    called_imsi_arr = request_json['params']['receiveImsi']
    sms_content = hex_to_int(str2hex(request_json['params']['smsContent']))
    identifier = 212
    # called_imsi_arr = all_called_imsi.split(',')
    for called_imsi_int in called_imsi_arr:
        called_imsi_str = str(called_imsi_int)
        called_imsi = hex_to_int(str_to_bcd(called_imsi_str))
        sms_send_struct = [3]
        sms_send_struct.extend(fix_arr_length([identifier], 4))
        sms_send_struct.extend([01])
        sms_send_struct.extend(fix_arr_length(calling_msisdn, 8))
        sms_send_struct.extend(fix_arr_length(called_imsi, 8))
        sms_send_struct.extend(get_content_length(sms_content))
        sms_send_struct.extend(fix_arr_length(sms_content, 500))
        post_sms_data(struct.pack(str(len(sms_send_struct)) + 'B', *sms_send_struct))


def receive_test_sms(called_imsi_str, identifier=122):
    """
    下发测试短信
    typedef struct _JsonTestSmsRecv{
        uint8_t msg_type;    // msg_type = 0x09
        uint8_t identifier[4];
        uint8_t device_id;
        uint8_t called_msisdn[8];
    }JsonTestSmsRecv;
    :param called_imsi_str:
    :param identifier:
    :return:
    """
    called_imsi = hex_to_int(str_to_bcd(called_imsi_str))
    sms_send_struct = [9]
    id_arr = fix_arr_length([identifier], 4)
    id_arr.reverse()
    sms_send_struct.extend(id_arr)
    sms_send_struct.extend([01])
    sms_send_struct.extend(fix_arr_length(called_imsi, 8))
    post_sms_data(struct.pack(str(len(sms_send_struct)) + 'B', *sms_send_struct))


def receive_sms_media_info(request_json):
    """
    下发彩信信
    标记位8(1个字节)
    http:// 10.2.15.12/NNlSptc9rMtD（30个字节）
    主叫号码（8个字节）
    被叫IMSI（8个字节）
    文件大小（4个字节）
    :param request_json:
    :return:
    """
    send_phone = str(request_json['params']['sendPhone'])
    calling_msisdn = hex_to_int(str_to_bcd(send_phone))
    called_imsi_arr = request_json['params']['receiveImsi']
    # called_imsi_arr = all_called_imsi.split(',')
    download_dir = '/root/sms_file'
    for _, called_phone in enumerate(called_imsi_arr):
        called_imsi_str = str(called_phone)
        sms_send_struct = [8]
        rand_url = 'http://10.2.15.12/' + utils.generate_random_string(12)
        logger.info(rand_url + '--' + called_imsi_str)
        send_url = hex_to_int(utils.ascii2hex(rand_url))
        sms_send_struct.extend(fix_arr_length(send_url, 30))
        called_imsi = hex_to_int(str_to_bcd(called_imsi_str))
        sms_send_struct.extend(fix_arr_length(calling_msisdn, 8))
        sms_send_struct.extend(fix_arr_length(called_imsi, 8))
        # 文件大小
        file_zie = os.path.getsize(os.path.join(download_dir, 'mmse'))
        sms_send_struct.extend(file_size_2_arr(file_zie))
        post_sms_data(struct.pack(str(len(sms_send_struct)) + 'B', *sms_send_struct))


def file_size_2_arr(file_zie):
    hex_file_size = int_to_hex_str(file_zie)
    data = []
    for idx in range(len(hex_file_size), 0, -2):
        if idx - 2 > 0:
            data.append(int(hex_file_size[idx - 2:idx], 16))
        else:
            data.append(int(hex_file_size[idx - 1], 16))
    result_arr = [0, 0, 0, 0]
    for idx in range(0, len(data)):
        result_arr[idx] = data[idx]
    result_arr.reverse()
    return result_arr


def recive_call_info(request_json):
    """
    下发语音
    :param request_json:
    :return:
    """
    calling_msisdn = hex_to_int(str2hex2(str(request_json['params']['calling_msisdn'])))
    all_called_imsi = request_json['params']['called_imsi']
    origin_file_name = str(request_json['params']['tone'])
    media_type = 'audio'
    video_file = ''
    if origin_file_name.endswith('.mp4'):
        media_type = 'video'
        video_file = origin_file_name
        audio_file = origin_file_name[0:origin_file_name.find('.mp4')] + '.amr'
    else:
        audio_file = origin_file_name
    audio_file_int = hex_to_int(str2hex2(audio_file))
    video_file_int = hex_to_int(str2hex2(video_file))
    media_type_int = hex_to_int(str2hex2(media_type))
    called_phone_list = get_phone_by_imsi(all_called_imsi)
    for idx, called_phone in enumerate(called_phone_list):
        origin_phone = called_phone
        called_phone = hex_to_int(str2hex2(called_phone))
        identifier = origin_phone + str(random.randint(0, 9999))
        identifier = hex_to_int(str2hex2(identifier))
        sms_send_struct = [7]
        sms_send_struct.extend(fix_arr_length(identifier, 32))  # callId
        sms_send_struct.extend(fix_arr_length(calling_msisdn, 32))  # calling
        sms_send_struct.extend(fix_arr_length(called_phone, 32))  # called
        sms_send_struct.extend(fix_arr_length(media_type_int, 16))  # audio / video
        sms_send_struct.extend(fix_arr_length(audio_file_int, 32))  # audioFile
        sms_send_struct.extend(fix_arr_length(video_file_int, 32))  # video File
        post_sms_data(struct.pack(str(len(sms_send_struct)) + 'B', *sms_send_struct), conf.sms_tone_port)


def get_phone_by_imsi(imsi_list):
    with open(conf.user_cfg_path, 'r') as f:
        data_list = f.read().strip().split('\n')
    imsi_phone_mapping = {}
    for line in data_list:
        line_arr = line.split(" ")
        imsi_phone_mapping[line_arr[0]] = line_arr[-1]
    rst = []
    for imsi in imsi_list:
        phone = imsi_phone_mapping.get(str(imsi))
        if phone and phone not in rst:
            rst.append(phone)
    return rst


def get_imsi_by_phone(phone):
    with open(conf.user_cfg_path, 'r') as f:
        data_list = f.read().strip().split('\n')
    for line in data_list:
        line_arr = line.split(" ")
        if str(phone) == line_arr[-1]:
            return line_arr[0]
    return None


def rec_auth_info(response_json):
    """
    收到授权
    :param response_json:
    :return:
    """
    params = response_json['params']
    r_identifier = params['identifier']
    # authquin = params['authquad']
    r_response = [0]
    ck_arr = hex_to_int(params['kasme'])
    r_response.extend(ck_arr)
    # ik_arr = hex_to_int(authquin['ik'])
    # r_response.extend(ik_arr)
    rand_arr = hex_to_int(params['rand'])
    r_response.extend(rand_arr)
    xres_arr = hex_to_int(params['xres'])
    r_response.extend(xres_arr)
    autn_arr = hex_to_int(params['autn'])
    r_response.extend(autn_arr)
    r_response.extend(hex_to_int(r_identifier))
    post_local_data(struct.pack(str(len(r_response)) + 'B', *r_response))


def rec_auth_ims(response_json):
    """
    收到授权--五元组
    :param response_json:
    :return:
    """
    params = response_json['params']
    r_identifier = params['identifier']
    # authquad = params['authquad']
    r_response = [2]
    ck_arr = hex_to_int(params['ck'])
    r_response.extend(ck_arr)
    ik_arr = hex_to_int(params['ik'])
    r_response.extend(ik_arr)
    rand_arr = hex_to_int(params['rand'])
    r_response.extend(rand_arr)
    xres_arr = hex_to_int(params['xres'])
    r_response.extend(xres_arr)
    autn_arr = hex_to_int(params['autn'])
    r_response.extend(autn_arr)
    r_response.extend(hex_to_int(r_identifier))
    post_local_data(struct.pack(str(len(r_response)) + 'B', *r_response), port=conf.sending_w_udp_port)


def start_server():
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(('0.0.0.0', conf.tcp_listen_port))
    while True:
        data, addr = sock.recvfrom(1024)
        msg = data.rstrip()
        msg_len = len(msg)
        try:
            if msg_len == 0:
                continue
            unpack_data = struct.unpack(str(msg_len) + 'B', msg)
            msg_type = unpack_data[0]
            logger.info('msg_type:' + str(msg_type) + ';msg_len=' + str(msg_len))
            # 上报业务
            if msg_type == 1 and msg_len == 36:
                send_user_info(unpack_data)
            # 长度 42-> 58
            if msg_type == 4 and msg_len == 58:
                send_network_user(unpack_data)
            # 授权业务
            if msg_type == 0 and msg_len == 47:
                send_auth_info(unpack_data)
            # 授权业务
            if msg_type == 2 and msg_len == 47:
                send_auth_ims(unpack_data)
            if msg_type == 3:
                send_sms_info(unpack_data)
            if msg_type == 5:
                send_calling_info(unpack_data)
            if msg_type == 6:
                send_outline_user(unpack_data)
            # 测试短信回调
            if msg_type == 9:
                test_sms_callback(unpack_data)

            logger.info('process one msg')
        except Exception as e:
            logger.error('recive msg err:' + str(e))
            logger.error(log_helper.format_exc_all())


def read_sms_log():
    root_path = os.path.dirname(os.path.realpath(__file__))
    sms_path = os.path.join(root_path, 'sms')
    sms_files = os.listdir(sms_path)
    ret_arr = []
    if len(sms_files) > 0:
        for sms_file in sms_files:
            try:
                sms_file_path = os.path.join(sms_path, sms_file)
                if os.access(sms_file_path, os.R_OK):
                    with open(sms_file_path, 'r') as f:
                        request_json = json.loads(f.read().strip())
                        calling_msisdn = hex_to_int(
                            str_to_bcd(request_json['params']['calling_msisdn']))
                        called_msisdn = hex_to_int(
                            str_to_bcd(request_json['params']['called_msisdn']))
                        # sms_length = hex_to_int(request_json['params']['sms_length'])
                        sms_content = hex_to_int(str2hex(request_json['params']['sms_content']))
                        identifier = request_json['params']['identifier']
                        sms_send_struct = [3]
                        sms_send_struct.extend(fix_arr_length([identifier], 4))
                        sms_send_struct.extend([01])
                        sms_send_struct.extend(fix_arr_length(calling_msisdn, 8))
                        sms_send_struct.extend(fix_arr_length(called_msisdn, 8))
                        sms_send_struct.extend([len(sms_content)])
                        sms_send_struct.extend(fix_arr_length(sms_content, 150))
                        ret_arr.append(
                            struct.pack(str(len(sms_send_struct)) + 'B', *sms_send_struct))
                    os.remove(sms_file_path)
            except Exception as e:
                logger.error(str(e))
                logger.error(log_helper.format_exc_all())
    return ret_arr


def send_auth_info(unpack_data):
    imsi = ''
    for imsi_idx in range(1, 9):
        tmp = to_bcd(unpack_data[imsi_idx])
        if tmp[1] == 'f':
            tmp = tmp[0]
            imsi += tmp
            break
        else:
            imsi += tmp
    plmn = ''
    for idx in range(9, 12):
        tmp = to_bcd(unpack_data[idx])
        if tmp[1] == 'f':
            tmp = tmp[0]
            plmn += tmp
            # break
        else:
            plmn += tmp
    re_syn_flag = unpack_data[12]
    rand = ''
    for idx in range(13, 29):
        # rand += to_normal(unpack_data[idx])
        rand += int_to_hex_str(unpack_data[idx])
    auts = ''
    for idx in range(29, 43):
        # auts += to_normal(unpack_data[idx])
        auts += int_to_hex_str(unpack_data[idx])
    identifier = ''
    for idx in range(43, 47):
        # identifier += to_normal(unpack_data[idx])
        identifier += int_to_hex_str(unpack_data[idx])
    device_id = ip_utils.get_device_info()
    request_param = {
        'cmdType': 'auth',
        'deviceId': device_id,
        'params': {
            'imsi': int(imsi),
            'plmn': int(plmn),
            'reSynFlag': int(re_syn_flag),
            'rand': rand,
            'auts': auts,
            'identifier': identifier
        }
    }
    print request_param
    # logger.info('request_pram:' + json.dumps(request_param, ensure_ascii=False))
    # send_initinfo.post(json.dumps(request_param), conf.udp_port)
    http_responser.response_json(request_param)


def send_auth_ims(unpack_data):
    """
    五元组
    """
    imsi = ''
    for imsi_idx in range(1, 9):
        tmp = to_bcd(unpack_data[imsi_idx])
        if tmp[1] == 'f':
            tmp = tmp[0]
            imsi += tmp
            break
        else:
            imsi += tmp
    plmn = ''
    for idx in range(9, 12):
        tmp = to_bcd(unpack_data[idx])
        if tmp[1] == 'f':
            tmp = tmp[0]
            plmn += tmp
            # break
        else:
            plmn += tmp
    re_syn_flag = unpack_data[12]
    rand = ''
    for idx in range(13, 29):
        # rand += to_normal(unpack_data[idx])
        rand += int_to_hex_str(unpack_data[idx])
    auts = ''
    for idx in range(29, 43):
        # auts += to_normal(unpack_data[idx])
        auts += int_to_hex_str(unpack_data[idx])
    identifier = ''
    for idx in range(43, 47):
        # identifier += to_normal(unpack_data[idx])
        identifier += int_to_hex_str(unpack_data[idx])
    device_id = ip_utils.get_device_info()
    request_param = {
        'cmdType': 'ims',  # authims-> ims
        'deviceId': device_id,
        'params': {
            'imsi': int(imsi),
            'plmn': int(plmn),
            'reSynFlag': int(re_syn_flag),
            'rand': rand,
            'auts': auts,
            'identifier': identifier
        }
    }
    print request_param
    # logger.info('request_pram:' + json.dumps(request_param, ensure_ascii=False))
    # send_initinfo.post(json.dumps(request_param), conf.udp_port)
    http_responser.response_json(request_param)


def send_sms_info(unpack_data):
    """
    发送短信
    :param unpack_data:
    :return:
    """
    identifier = ''
    for idx in range(1, 5):
        identifier += to_normal(unpack_data[idx])
    calling_msisdn = ''
    for idx in range(6, 14):
        tmp = to_bcd(unpack_data[idx])
        if tmp[1] == 'f':
            tmp == tmp[0]
            calling_msisdn += tmp
            break
        else:
            calling_msisdn += tmp
    calling_msisdn = calling_msisdn.replace('f', '')
    called_msisdn = ''
    for idx in range(14, 22):
        tmp = to_bcd(unpack_data[idx])
        if tmp[1] == 'f':
            tmp == tmp[0]
            called_msisdn += tmp
            break
        else:
            called_msisdn += tmp
    called_msisdn = called_msisdn.replace('f', '')
    sms_length = int(str(unpack_data[23]) + str(unpack_data[22]))
    sms_content = ''
    for idx in range(24, 25 + sms_length * 2):
        sms_content += to_normal2(unpack_data[idx])
    device_id = ip_utils.get_device_info()
    calling_imsi = get_imsi_by_phone(calling_msisdn)
    if calling_imsi is None:
        calling_imsi = calling_msisdn
    request_param = {
        'cmdType': 'smsIntercept',
        'deviceId': device_id,
        'params': {
            'sendImsi': int(calling_imsi),
            'receivePhone': int(called_msisdn),
            'smsLength': sms_length,
            'smsContent': hex2hanzi(sms_content).encode('utf-8')
        }
    }
    http_responser.response_json(request_param)


def send_user_info(unpack_data):
    imsi = ''
    for imsi_idx in range(1, 9):
        tmp = to_bcd(unpack_data[imsi_idx])
        if tmp[1] == 'f':
            tmp = tmp[0]
            imsi += tmp
            break
        else:
            imsi += tmp
    imsi = imsi.replace('f', '')
    imei = ''
    for idx in range(9, 17):
        imei += to_normal(unpack_data[idx])
    report_time = ''
    for idx in range(17, 36):
        report_time += to_normal(unpack_data[idx])
    device_id = ip_utils.get_device_info()
    request_param = {
        'cmdType': 'register',
        'deviceId': device_id,
        'params': {
            'imsi': int(imsi),
            'imei': imei,
            'time': ascii2hanzi(report_time)
        }
    }
    # print request_param
    # logger.info('request_pram:' + json.dumps(request_param, ensure_ascii=False))
    # send_initinfo.post(json.dumps(request_param), conf.udp_port)
    http_responser.response_json(request_param)


def send_network_user(unpack_data):
    imsi = ''
    for imsi_idx in range(1, 9):
        tmp = to_bcd(unpack_data[imsi_idx])
        if tmp[1] == 'f':
            tmp = tmp[0]
            imsi += tmp
            break
        else:
            imsi += tmp
        # imsi += to_bcd(unpack_data[imsi_idx])
    imei = ''
    for idx in range(9, 17):
        imei += to_bcd(unpack_data[idx])
    imei = imei[1:15] + luhn_check(imei[1:16])
    # ip_v4 18-22 ip_v6 22-38
    ip_net = ''
    for idx in range(18, 22):
        ip_net += to_normal2(unpack_data[idx])
    if int(ip_net, 16) == 0:
        ip_net = ''
        for idx in range(22, 38):
            ip_net += to_normal2(unpack_data[idx])
    logger.info("收到的ip信息=" + ip_net)
    ip = IPy.IP(int(ip_net, 16)).strCompressed()
    logger.info("转化后的ip=" + ip)
    status = get_ip_type(ip)
    # time 19位
    u_time = ''
    for idx in range(38, 57):
        u_time += to_normal(unpack_data[idx])
    device_id = ip_utils.get_device_info()
    request_param = {
        'cmdType': 'netuser',
        'deviceId': device_id,
        'params': {
            'imsi': int(imsi),
            'imei': imei,
            'type': status,
            'ip': ip,
            'time': ascii2hanzi(u_time)
        }
    }
    save_network_user(imsi, ip)
    # print request_param
    # logger.info('request_pram:' + json.dumps(request_param, ensure_ascii=False))
    # send_initinfo.post(json.dumps(request_param), conf.udp_port)
    http_responser.response_json(request_param)


def luhn_check(data):
    old_arr = [int(data[i]) for i in range(len(data) - 3, -1, -2)]
    even_arr = []
    for i in range(len(data) - 2, -1, -2):
        val = int(data[i]) * 2
        if val > 9:
            even_arr.append(val - 9)
        else:
            even_arr.append(val)
    total = sum(old_arr) + sum(even_arr)
    return str(total * 9)[-1]


def read_network_user():
    if os.path.exists(conf.network_user_path):
        with open(conf.network_user_path, 'r') as f:
            return json.load(f)
    return []


def update_network_user_file(json_data):
    with open(conf.network_user_path, 'w') as f:
        json.dump(json_data, f)


def save_network_user(imsi, ip):
    with file_lock:
        net_work_user = read_network_user()
        exist_user = False
        for item in net_work_user:
            if item.get('imsi', '') == imsi and item.get('ip', '') == ip:
                exist_user = True
                break
            if item.get('ip', '') == ip:
                net_work_user.remove(item)
                send_underline_user(item.get('imsi'), item.get('ip'))
                break
        if not exist_user:
            net_work_user.append({
                'imsi': imsi,
                'ip': ip,
                'check_count': 0,
                'reachable': True,
                'time': time.time()
            })
            update_network_user_file(net_work_user)


def send_calling_info(unpack_data):
    """
    struct call_report{
         unsigned char msg_type;      /* 枚举类型，为5表示上报呼叫事件 */
        char time [19];              /* 格式：YYYY-MM-DD HH-MM-SS*/
        unsigned char calling[10];      /* 主叫号码，目前为主叫的IMSI，反序BCD格式 */
        unsigned char called[10];    /* 被叫号码，目前为被叫的MSISDN，反序BCD格式 */
        unsigned char event;          /* 1表示呼叫开始，0表示呼叫结束 */
    };
    :param unpack_data:
    :return:
    """
    u_time = ''
    for idx in range(1, 20):
        u_time += to_normal(unpack_data[idx])
    calling_msisdn = ''
    for idx in range(21, 53):
        tmp = to_normal(unpack_data[idx])
        if tmp == '00':
            continue
        calling_msisdn += tmp
    calling_msisdn = ascii2hanzi(calling_msisdn)
    called_msisdn = ''
    for idx in range(53, 85):
        tmp = to_normal(unpack_data[idx])
        if tmp == '00':
            continue
        called_msisdn += tmp
    called_msisdn = ascii2hanzi(called_msisdn)
    status = int(unpack_data[85])
    time_han = ascii2hanzi(u_time)
    time_fmt = time.strptime(time_han, '%Y-%m-%d %H:%M:%S')
    timestamp = int(time.mktime(time_fmt) * 1000)
    device_id = ip_utils.get_device_info()
    called_imsi = get_imsi_by_phone(called_msisdn)
    if called_imsi is None:
        called_imsi = called_msisdn
    calling_imsi = get_imsi_by_phone(calling_msisdn)
    if calling_imsi is None:
        calling_imsi = calling_msisdn
    call_status = 0
    # 开始
    if status == 1:
        call_status = 1
    # 结束
    if status == 0:
        call_status = 2
    # 异常
    if status == 2:
        call_status = 3
    request_param = {
        'cmdType': 'callReport',
        'device_id': device_id,
        'params': {
            'time': timestamp,
            'calling': int(calling_imsi),
            'called': int(called_imsi),
            'status': call_status
        }
    }
    # print request_param
    # logger.info('request_pram:' + json.dumps(request_param, ensure_ascii=False))
    # send_initinfo.post(json.dumps(request_param), conf.udp_port)
    http_responser.response_json(request_param)


def test_sms_callback(unpack_data):
    """
    测试短信回调
    typedef struct _JsonTestSmsReport{
         uint8_t msg_type;   // msg_type = 0x09
         uint8_t identifier[4];
         uint8_t device_id;
         uint8_t called_msisdn[8];
         uint8_t testResult; // 1表示成功, 0表示失败
    }JsonTestSmsReport;
    """
    identifier = ''
    for idx in range(1, 5):
        identifier += to_normal2(unpack_data[idx])
    device_id = to_normal2(unpack_data[5])
    imsi = ''
    for imsi_idx in range(6, 14):
        imsi += to_bcd(unpack_data[imsi_idx])
    if imsi.endswith('f'):
        imsi = imsi[:-1]
    # 状态 0 下线 1 正常
    status = int(to_normal2(unpack_data[14]))
    logger.info("test_sms callback:msg_type=9,imsi=" + str(
        imsi) + ";status=" + str(status) + ";identifier=" + identifier + ";device_id=" + str(device_id))
    net_work_user = read_network_user()
    under_user_item = []
    new_item_data = []
    for item in net_work_user:
        if item.get('imsi', '') == imsi:
            item['time'] = time.time()
            logger.info('当前imsi+ip：' + str(imsi) + ',' + str(item['ip']) + ';检测数量' + str(item['check_count']))
            # 正常 ，重置check_count
            if status == 1:
                item['reachable'] = True
                item['check_count'] = 0
            else:
                item['reachable'] = False
                item['check_count'] = item['check_count'] + 1
            if not item['reachable'] and item['check_count'] >= 2:
                under_user_item.append(item)
                continue
        new_item_data.append(item)
    update_network_user_file(new_item_data)
    for item in under_user_item:
        logger.info('已下线：' + str(item['imsi']) + ' ip=' + str(item['ip']))
        send_underline_user(item['imsi'], item['ip'])


def send_outline_user(unpack_data):
    imsi = ''
    for imsi_idx in range(1, 9):
        imsi += to_bcd(unpack_data[imsi_idx])
    if imsi.endswith('f'):
        imsi = imsi[:-1]
    logger.info("msg_type=6,imsi=" + str(imsi))
    net_work_data = read_network_user()
    if len(net_work_data) == 0:
        return
    under_user_item = []
    other_list = []
    for item in net_work_data:
        if item.get('imsi', '') == imsi:
            under_user_item.append(item)
        else:
            other_list.append(item)
    for item in under_user_item:
        logger.info('接受到下线上报：' + str(item['imsi']) + ' ip=' + str(item['ip']))
        send_underline_user(imsi, item['ip'])
    update_network_user_file(other_list)


def send_underline_user(imsi, ip, ip_type=None):
    device_id = ip_utils.get_device_info()
    if ip_type is None:
        ip_type = get_ip_type(ip)
    request_param = {
        'cmdType': 'underLine',
        'deviceId': device_id,
        'params': {
            'imsi': int(imsi),
            'ip': ip,
            'type': ip_type
        }
    }
    logger.info(json.dumps(request_param, ensure_ascii=False))
    http_responser.response_json(request_param)


def get_ip_type(ip):
    """
    1代表LTE关机 2代表ims关机
    """
    conf_data = query_conf(conf.lte_yaml_path)
    conf_addr = conf_data['smf']['subnet'][0]['addr']
    # for conf_data_item in conf_data['smf']['subnet']:
    #     if conf_data_item['dnn'] == 'mobile':
    #         conf_addr = conf_data_item['addr']
    status = 1
    if conf_addr:
        left_addr = conf_addr[0:conf_addr.rfind('.')]
        tmp_ip = ip[0:ip.rfind('.')]
        if tmp_ip == left_addr:
            status = 1
        else:
            status = 2
    return status


def reset_network_time():
    net_work_data = read_network_user()
    if len(net_work_data) == 0:
        return
    for idx, item in enumerate(net_work_data):
        item['time'] = time.time()
        item['reachable'] = True
        item['check_count'] = 0
    update_network_user_file(net_work_data)


def check_under_user_by_sms(start_identifier):
    net_work_data = read_network_user()
    if len(net_work_data) == 0:
        return
    under_user_item = []
    new_item_data = []
    send_img = []
    for idx, item in enumerate(net_work_data):
        imsi = item.get('imsi', '')
        last_time = item.get('time')
        cur_time = time.time()
        logger.info(str(imsi) + '时间差' + str(int(cur_time - last_time)))
        if int(cur_time - last_time) > conf.network_user_check_time:
            logger.info(str(imsi) + '回调超时')
            item['check_count'] = item['check_count'] + 1
            item['reachable'] = False
        # 用户下线
        if not item['reachable'] and item['check_count'] >= 2:
            under_user_item.append(item)
        else:
            item['time'] = cur_time
            new_item_data.append(item)
            # 发送测试短信
            if imsi not in send_img:
                send_img.append(imsi)
                logger.info('start send_test_sms:' + str(imsi) + ';identifier=' + str(start_identifier + idx))
                receive_test_sms(str(imsi), start_identifier + idx)
    update_network_user_file(new_item_data)
    for item in under_user_item:
        logger.info('已下线：' + str(item['imsi']) + ' ip=' + str(item['ip']))
        send_underline_user(item['imsi'], item['ip'])


def check_under_user():
    net_work_data = read_network_user()
    if len(net_work_data) == 0:
        return
    ip_list = []
    for item in net_work_data:
        ip_list.append(item['ip'])
    if len(ip_list) == 0:
        return
    logger.info('check ip :' + ' '.join(ip_list))
    check_ip_result = []
    executor = Pool(4)
    result_data = executor.imap(ip_utils.is_reachable, ip_list)
    for reach_data in result_data:
        check_ip_result.append(reach_data)
    executor.close()
    ip_index_map = {}
    for idx, item in enumerate(ip_list):
        ip_index_map[item] = check_ip_result[idx]
    new_item_data = []
    under_user_item = []
    for item in net_work_data:
        item['time'] = time.time()
        is_reach = ip_index_map[item['ip']]
        item['reachable'] = is_reach
        if not is_reach:
            item['check_count'] = item['check_count'] + 1
        else:
            item['check_count'] = 0
        if not is_reach and item['check_count'] == 2:
            under_user_item.append(item)
        else:
            new_item_data.append(item)
    update_network_user_file(new_item_data)
    for item in under_user_item:
        logger.info('已下线：' + str(item['imsi']) + ' ip=' + str(item['ip']))
        send_underline_user(item['imsi'], item['ip'])


def check_under_user_loop():
    start_identifier = 1
    reset_network_time()
    while True:
        logger.info('start check user online ....')
        try:
            # check_under_user()
            check_under_user_by_sms(start_identifier)
            start_identifier += 1
            if start_identifier >= 250:
                start_identifier = 1
        except Exception as e:
            logger.error("check user error" + str(e) + log_helper.format_exc_all())
        time.sleep(conf.network_user_check_time)


def start_self():
    thread.start_new_thread(check_under_user_loop, ())
    while True:
        try:
            start_server()
        except Exception as e:
            logger.error('start_server error:' + str(e))
        time.sleep(2)


if __name__ == '__main__':
    # # start_self()
    # auth1 = "05323032322d30372d31362031333a30313a3039003131313135353100000000000000000000000000000000000000000000000000383532363832393631303600000000000000000000000000000000000000000001"
    # unpack_data = []
    # for i in range(0, len(auth1), 2):
    #     unpack_data.append(int(auth1[i] + auth1[i + 1], 16))
    # send_calling_info(unpack_data)
    # for idx, data in enumerate(unpack_data):
    #     print idx, data
    # rand = ''
    # for idx in range(13, 29):
    #     print hex(unpack_data[idx]), int_to_hex_str(unpack_data[idx])
    #     rand += int_to_hex_str(unpack_data[idx])
    # auts = ''
    # for idx in range(29, 43):
    #     auts += int_to_hex_str(unpack_data[idx])
    # # print auts
    # print rand
    # executor = Pool(3)
    # ip = ['192.168.1.1', '192.168.11.160', '192.168.11.200']
    # result = executor.imap(ip_utils.is_reachable, ip)
    # for item in result:
    #     print item
    # print hex_to_int(str2hex('1234.amr'))
    # print str(random.randint(0, 9999))
    print hex2hanzi('0031')
